package moonlit.chill.pay.service.impl.wechat;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import lombok.extern.slf4j.Slf4j;
import moonlit.chill.pay.cache.PayConfigDataCache;
import moonlit.chill.pay.constants.Constants;
import moonlit.chill.pay.constants.PayType;
import moonlit.chill.pay.service.PayStrategy;
import moonlit.chill.pay.util.WxCertUtil;
import moonlit.chill.pay.util.WxConfigUtil;
import moonlit.chill.pay.vo.PayConfig;
import moonlit.chill.pay.vo.PayResult;
import moonlit.chill.pay.vo.PayResultForNotify;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * @author MoonlitChill
 * @date 2024/4/16 08:55
 */
@Slf4j
@Service("wxNotifyStrategy")
public class WxNotifyStrategyServiceImpl implements PayStrategy {

    @Resource
    private PayConfigDataCache payConfigDataCache;

    @Autowired
    private WxConfigUtil wxConfigUtil;

    private static final String TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX";

    @Override
    public PayResult<?> payForNotify(HttpServletRequest request, HttpServletResponse response) {
        PayResult<String> result = new PayResult<>();
        Map<String, String> map = new HashMap<>(12);
        response.setStatus(500);
        map.put("code", "ERROR");
        map.put("message", "回调异常");
        BufferedReader reader = null;
        try {
            PayConfig payConfig = payConfigDataCache.getPayConfig();
            String wechatPaySerial = request.getHeader("Wechatpay-Serial");
            String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
            String wechatTimestamp = request.getHeader("Wechatpay-Timestamp");
            String wechatSignature = request.getHeader("Wechatpay-Signature");
            log.info("微信异步回调serialNo：{}，nonce：{}，timestamp：{}，signature：{}", wechatPaySerial, wechatpayNonce, wechatTimestamp, wechatSignature);
            reader = request.getReader();
            StringBuilder builder = new StringBuilder();
            String line = reader.readLine();
            while (line != null) {
                builder.append(line);
                line = reader.readLine();
            }
            reader.close();
            String reqBody = builder.toString();
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(wechatPaySerial)
                    .nonce(wechatpayNonce)
                    .signature(wechatSignature)
                    .timestamp(wechatTimestamp)
                    .body(reqBody)
                    .build();
            log.info("微信异步回调密文 {}", requestParam);
            NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(payConfig.getUId())
                    .privateKeyFromPath(wxConfigUtil.keyCertPath(payConfig))
                    .merchantSerialNumber(WxCertUtil.getCertificateSerialNumber(wxConfigUtil.certPath(payConfig)))
                    .apiV3Key(payConfig.getCertKey())
                    .build();
            NotificationParser parser = new NotificationParser(config);
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            log.info("微信异步回调数据：{}", JSONUtil.toJsonStr(transaction));
            if (ObjectUtil.isNotEmpty(transaction)) {
                PayResultForNotify payResultForNotify = new PayResultForNotify();
                if (transaction.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)) {
                    payResultForNotify.setPayTime(DateUtil.format(DateUtil.parse(transaction.getSuccessTime(), TIME_FORMAT), DatePattern.NORM_DATETIME_PATTERN));
                    payResultForNotify.setTransNum(transaction.getOutTradeNo());
                    payResultForNotify.setBody(transaction.getAttach());
                    payResultForNotify.setOutTradeNo(transaction.getTransactionId());
                    BigDecimal amountPay = (new BigDecimal(transaction.getAmount().getTotal()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
                    payResultForNotify.setPayAmount(amountPay);
                    payResultForNotify.setPayType(PayType.WX);
                    result.setSuccess(Boolean.TRUE);
                    result.setResult(JSONUtil.toJsonStr(payResultForNotify));
                } else {
                    result.error(transaction.getTradeStateDesc(), Constants.PAY_FAIL_CODE);
                }
                response.setStatus(HttpStatus.HTTP_OK);
                map.put("code", "SUCCESS");
                map.put("message", "SUCCESS");
            } else {
                result.error("微信异步回调失败", Constants.PAY_FAIL_CODE);
            }
        } catch (Exception e) {
            result.errorWithException("微信异步回调异常，请联系管理员", Constants.PAY_ERROR_CODE, e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                    response.setHeader("Content-type", ContentType.JSON.toString());
                    response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
                    response.flushBuffer();
                } catch (IOException e) {
                    log.error("微信异步回调IO异常", e);
                }
            }
        }
        return result;
    }
}
